package com.example.aspect;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.IdUtil;
import com.example.common.OperateLog;
import com.example.po.OperateLogPo;
import com.example.service.OperateLogService;
import com.example.utils.IpAddressUtils;
import com.example.utils.IpUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
/*
@description:操作日志切面
@ClassName OperateLogAspect
@author chen
@create 2025-07-15 11:16
@Version 1.0
*/
@Aspect
@Order
@Component
public class OperateLogAspect
{
    @Resource
    private OperateLogService operateLogService;

    private final ObjectMapper mapper = new ObjectMapper();


    /*
     * @param
     * @==============History===============<br/>;
     * @Description //   环绕通知，拦截Controller中所有方法上标注有 @OperateLog注解的方法，记录日志
     * @Date 2025/7/15
     * @Author < a href="chencheng@prmt.net">chen</ a>;
     */
    @Around("@annotation(operateLog) && execution(* com.example..*Controller.*(..))")
    public Object around(ProceedingJoinPoint joinPoint, OperateLog operateLog) throws Throwable
    {
        long startTime = System.currentTimeMillis();
        Object result = null;
        Throwable error = null;
        try
        {
            result = joinPoint.proceed();
        } catch (Throwable e)
        {
            error = e;
            throw e;
        } finally
        {
            this.log(joinPoint, operateLog, result, error, startTime);
        }
        return result;
    }


    private void log(ProceedingJoinPoint joinPoint, OperateLog operLog, Object result, Throwable error, long startTime) throws JsonProcessingException
    {
        OperateLogPo operateLogPo = new OperateLogPo();
        // 日志id
        operateLogPo.setId(IdUtil.fastSimpleUUID());
        // 日志信息，直接从注解中获取
        operateLogPo.setLog(operLog.log());
        // 接口参数，json格式
        operateLogPo.setParamJson(jsonString(getParamMap(joinPoint)));
        // 返回值，json格式
        operateLogPo.setResultJson(jsonString(result));
        // 状态，0：异常，1：正常，error不为空表示有异常
        operateLogPo.setStatus(error != null ? 0 : 1);
        // 记录异常信息
        if (error != null)
        {
            operateLogPo.setErrorMsg(ExceptionUtil.stacktraceToString(error));
        }
        operateLogPo.setCostTime(System.currentTimeMillis() - startTime);
        // 操作ip
        String operateIp = IpUtils.getIpAddr();
        operateLogPo.setOperIp(operateIp);
        // 根据ip获取ip归属地
        operateLogPo.setOperIpAddress(IpAddressUtils.getRegion(operateIp));
        // 获取用户名可以自己去实现
        operateLogPo.setOperUserName("");
        // 操作时间
        operateLogPo.setOperTime(LocalDateTime.now());
        // 写入日志
        this.operateLogService.save(operateLogPo);
    }






    private Map<String, Object> getParamMap(ProceedingJoinPoint joinPoint)
    {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取所有要打印的参数，丢到map中，key为参数名称，value为参数的值，然后会将这个map以json格式输出
        Map<String, Object> paramMap = new LinkedHashMap<>();
        String[] parameterNames = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++)
        {
            // 参数名称
            String parameterName = parameterNames[i];
            // 参数值
            Object parameterValue = args[i];
            // 将其放入到map中，稍后会以json格式输出
            paramMap.put(parameterName, parameterValue);
        }
        return paramMap;
    }


    private String jsonString(Object obj) throws JsonProcessingException
    {
        if (obj == null)
        {
            return null;
        }
        return mapper.writeValueAsString(obj);
    }

}
